# set up notebook to display multiple output in one cell
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
If you do not have Pillow or opencv installed, you will need to run the following two cells first.
!pip install Pillow
Requirement already satisfied: Pillow in c:\users\rites\anaconda\lib\site-packages (9.4.0)
pip install opencv-python
Requirement already satisfied: opencv-python in c:\users\rites\anaconda\lib\site-packages (4.8.0.76) Requirement already satisfied: numpy>=1.17.0 in c:\users\rites\anaconda\lib\site-packages (from opencv-python) (1.23.5) Note: you may need to restart the kernel to use updated packages.
# import libraries needed
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm #use for colors
from pylab import imread
from PIL import Image, ImageOps, ImageFilter
import cv2
(reference: https://en.wikipedia.org/wiki/Color)
Let's start out with the basics of images and see how the combination of red, green and blue (RGB) creates specific colors.
A color image can be converted to a 3D-array with shape (x, y, 3) which indicates that the 3D-array contains x 2D-arrays and each 2D-array contains y 1D-arrays and each 1D-array has size 3 (i.e., a triple of numbers from 0 to 255). Geometrically, we represent a color image as ax by y grid of pixels and each pixel consists of three numbers from 0 to 255. The first number denotes the intensity of the red color, the second number the intensity of the green color and the third number the intensity of the blue color.**
An array of all zeros [0,0,0] will generate an image with all pixels black.
(Reference: Introduction to Digital Images)
# change settings for new color
red = 0
green = 0
blue = 255
# Create a square of 15 rows and 15 columns (pixels)
# The 3 is the size of the third dimension of the array: (r,g,b)
color_test = np.zeros(shape=(15, 15, 3))
# position 0 is red, 1 is green and 2 is blue
color_test[:,:,0] = red # assign the value of red to all the values in pixel position 0
color_test[:,:,1] = green # assign the value of green to all the values in pixel position 1
color_test[:,:,2] = blue # assign the value of green to all the values in pixel position 2
plt.imshow(color_test/255);
Notice above that we divided color_test by 255 and used this to display the image. The reason this is done is because the object color_test is dtype of float which does not correctly reflect an image. So we need to scale this by either dividing by 255 or by changing the object to a uint8 dtype.
To change the object type we could replace the last line of code with the following:
color_test = color_test.astype('uint8')
plt.imshow(color_test);
color_test[5,5,1] = 255
color_test[5,5,1] = 255 # add in green to create an aqua pixel
plt.imshow(color_test/255)
<matplotlib.image.AxesImage at 0x23c4db7b3a0>
We can also try other colors for the 5,5 pixel. To create a magenta pixel, we would have to rerun the solid blue cell that we created initially. Then run the following code:
color_test[5,5,0] = 255
plt.imshow(color_test/255)
Below we are looking at three distinct pixels to see the breakdown of R, G, B at the 0,0 position, the 5,5 position, and the 14,0 position. The first and last of those are all blue with no red or green. The pixel at position 5,5 is the one we changed above.
# here we are looking at 3 individual pixels - R, G, B
color_test[0,0,:]
color_test[5,5,:]
color_test[14,0,:]
array([ 0., 0., 255.])
array([ 0., 255., 255.])
array([ 0., 0., 255.])
An image is represented in Python by a numpy array. A grayscale image is represented by a 2D-array, while a color image is represented by a 3D-array. For a color image each pixel is then a list of three values - the Red value, the Green value, and the Blue value. The values for and mixture of these three colors then specify the final color that should appear in the pixel (if there are more than three then the image is using a colorscheme that is not RGB).
Next we will read in a photo using imread() from pylab. The photo is of a vegetable stand at a market. Note: The photos used in this assignment for demonstration purposes are copyright free from Pexels.
# read in image
veggies = plt.imread('M8 Images/pexels-maria-orlova-4946998.jpg')
# this code will display the image within Jupyter Notebook
plt.imshow(veggies)
<matplotlib.image.AxesImage at 0x23c4dc2b2e0>
In this next code cell we take a closer look at the structure and makeup of veggies.
type called numpy ndarray.veggies photo has the shape of 1856 rows (height in pixels) by 2784 columns (width in pixels) with 3 colors (depth or color channel).dtype of the object is uint8 (unsigned integer 8 bit) which allows a range of values from 0 to 255.array displayed from the last line of code below shows the color for each pixel that makes up the image. This lists the R, G, B values for each pixel starting at the 0,0 position working across row 0 and ending with the last row and the 1855,2783 position.print("---------- type of object ----------")
type(veggies)
print("---------- shape of object ----------")
veggies.shape
print("---------- data type of object ----------")
veggies.dtype
print("---------- values within object ----------")
veggies
---------- type of object ----------
numpy.ndarray
---------- shape of object ----------
(1856, 2784, 3)
---------- data type of object ----------
dtype('uint8')
---------- values within object ----------
array([[[ 0, 31, 26],
[ 0, 31, 26],
[ 0, 32, 27],
...,
[ 96, 106, 69],
[ 97, 107, 70],
[ 97, 107, 70]],
[[ 0, 32, 27],
[ 0, 32, 27],
[ 0, 32, 27],
...,
[ 95, 105, 68],
[ 95, 105, 68],
[ 95, 105, 68]],
[[ 0, 32, 27],
[ 0, 32, 27],
[ 1, 33, 28],
...,
[ 94, 104, 67],
[ 94, 104, 67],
[ 94, 104, 67]],
...,
[[ 29, 117, 39],
[ 35, 114, 31],
[ 48, 111, 22],
...,
[149, 68, 74],
[150, 68, 74],
[155, 73, 79]],
[[ 36, 117, 38],
[ 43, 111, 26],
[ 61, 108, 14],
...,
[151, 70, 76],
[152, 70, 76],
[156, 74, 80]],
[[ 37, 114, 36],
[ 46, 108, 22],
[ 65, 105, 8],
...,
[153, 72, 78],
[153, 70, 78],
[157, 74, 82]]], dtype=uint8)
Now let's take a look at four specific pixels and display their color values.
# color values of the first 3 pixels of row 0
veggies[0,0,:]
veggies[0,1,:]
veggies[0,2,:]
# color values of the last pixel of row 0
y = veggies.shape[1] - 1 # get the y coordinates of the pixel's position
print('Last y coordinate is: ', y)
veggies[0, y, :]
array([ 0, 31, 26], dtype=uint8)
array([ 0, 31, 26], dtype=uint8)
array([ 0, 32, 27], dtype=uint8)
Last y coordinate is: 2783
array([ 97, 107, 70], dtype=uint8)
Matplotlib uses the viridis color palette as the default:
Below you can see when R, G, B colors are isolated with the default color palette.
# have 3 subplots for separating colors
fig, ax = plt.subplots(nrows = 1, ncols = 3, figsize = (12, 6))
colors = [cm.Reds, cm.Greens, cm.Blues]
header = ['Red only', 'Green only', 'Blue only']
for i in range(3):
ax[i].imshow(veggies[:,:,i])
ax[i].set_title(header[i])
plt.show()
# show unaltered picture
plt.imshow(veggies);
To really see each layer of color, we can change the Matplotlib color map (cm) to match.
# have 3 subplots for separating colors
fig, ax = plt.subplots(nrows = 1, ncols = 3, figsize = (12, 6))
colors = [cm.Reds, cm.Greens, cm.Blues]
header = ['Red only', 'Green only', 'Blue only']
for i in range(3):
ax[i].imshow(veggies[:,:,i], cmap = colors[i])
ax[i].set_title(header[i])
plt.show()
# show unaltered picture
plt.imshow(veggies);
We could plot only a portion of the image (using the built-in slicing)
# show the lower left corner of the photo
plt.imshow(veggies[1000:1750, 0:1200, :]);
And here is another example of isolating only a portion of the image.
# show the upper right corner of the photo with all colors
plt.imshow(veggies[250:800, 2000:2800, :]);
If we want this to be a new image, we can simply save this to a new object.
# you can save the portion of the image to a new object
sunflowers = veggies[750:1200, 2000:2800, :]
plt.imshow(sunflowers)
<matplotlib.image.AxesImage at 0x23c53adbd30>
#Todo: Isolate and display the sign from 'veggies' that reads 'Poiveons Rouge...'
p_rouge = veggies[730:1080, 680:1100, :]
plt.imshow(p_rouge)
<matplotlib.image.AxesImage at 0x23c53b0b5e0>
This next example adds a red bar down the middle of the image. The bar runs from rows 100 to 1750 (most of the height of the photo) and is 100 units wide from column 1400 to 1500 (approximately the center of the photo). Feel free to adjust the width, height or both to see how this changes the size and location of the bar.
# can alter pic by adding a line
temp = veggies.copy()
temp[100:1750, 1400:1500, :] = [255, 0, 0] # [red, green, blue]
plt.imshow(temp);
# load in new image for another example
balloons = plt.imread('M8 Images/pexels-tabitha-mort-693490.jpg')
plt.imshow(balloons );
# take a portion of an image
small = balloons [1500:2800,800:2200,:]
small.shape
plt.imshow(small)
(1300, 1400, 3)
<matplotlib.image.AxesImage at 0x23c52d2c0d0>
Note that in the example below we are taking a portion of one balloon and adding it back into the photo in a new location. Also note that the pixels in both sides of the equation are matching in size.
# and overlay it onto the orginal image
b3 = balloons.copy()
b3[500:1700,0:1400,:] = small[0:1200,0:1400,:]
plt.imshow(b3)
<matplotlib.image.AxesImage at 0x23c52df8850>
# this cell is provided for you and reads in the image for this problem
p2 = plt.imread('M8 Images/pexels-cottonbro-7243967.jpg')
plt.imshow(p2);
# Todo - isolate one head and display the image
head_1 = p2 [1900:2800,350:1100,:]
head_1.shape
plt.imshow(head_1)
(900, 750, 3)
<matplotlib.image.AxesImage at 0x23c53422650>
# Todo - isolate the second head and display the image
head_2 = p2 [2400:3300,1500:2250,:]
head_2.shape
plt.imshow(head_2)
(900, 750, 3)
<matplotlib.image.AxesImage at 0x23c52e57850>
# Todo - swap the heads and display the new image
p3 = p2.copy()
p3 [1900:2800,350:1100,:] = head_2
p3 [2400:3300,1500:2250,:] = head_1
plt.imshow(p3)
<matplotlib.image.AxesImage at 0x23c53498ac0>
The most familiar grayscale image is an xray.
# test using code from tutorial
# https://numpy.org/numpy-tutorials/content/tutorial-x-ray-image-processing.html
xray = plt.imread('M8 Images/xray_test.png')
plt.imshow(xray, cmap="gray")
plt.axis("off")
plt.show()
<matplotlib.image.AxesImage at 0x23c53911c30>
(-0.5, 1023.5, 1023.5, -0.5)
There are a number of ways to create a grayscale image. One way is shown below using a grayscale conversion formula. There are a few interesting factors about this conversion:
# gray scale conversion
gray = veggies.copy()
R, G, B = gray[:,:,0], gray[:,:,1], gray[:,:,2]
imGray = 0.2989 * R + 0.5870 * G + 0.1140 * B
plt.imshow(imGray, cmap='gray')
plt.show()
<matplotlib.image.AxesImage at 0x23c539118a0>
# two dimensional array
imGray.shape
# one color per pixel
imGray
(1856, 2784)
array([[21.161 , 21.161 , 21.862 , ..., 98.7824, 99.7823, 99.7823],
[21.862 , 21.862 , 21.862 , ..., 97.7825, 97.7825, 97.7825],
[21.862 , 21.862 , 22.8619, ..., 96.7826, 96.7826, 96.7826],
...,
[81.7931, 80.9135, 82.0122, ..., 92.8881, 93.187 , 98.1865],
[83.7714, 80.9737, 83.2249, ..., 94.8879, 95.1868, 99.1864],
[82.0813, 79.6534, 81.9755, ..., 96.8877, 95.7137, 99.7133]])
As you might expect, we can also flip our photo vertically or horizontally, or both. This next example flips the photo along the vertical axis. In the example following this, our photo is flipped along the horizontal axis.
# flip along the vertical axis; first make a copy of the image
flipv = veggies.copy()
flipv.shape # 1856 height, 2784 width
# goal is to flipv on y axis
# get counter from shape
num = flipv.shape[1] - 1 #adjust for zero offset
print(f'x axis goes from 0 to {num}')
for i in range(0,num):
#print('i is: ',i)
# take what is in position 2782 and move it to 0, etc
flipv[:,i,:] = veggies[:,num-i,:]
plt.imshow(flipv)
(1856, 2784, 3)
x axis goes from 0 to 2783
<matplotlib.image.AxesImage at 0x23c519224d0>
# flip along the horizontal axis; first make a copy of the image
fliph = veggies.copy()
fliph.shape
# get counter from shape tuple to int
num = fliph.shape[0] - 1
print(num)
for i in range(0,num):
#print('i is: ',i)
fliph[i,:,:] = veggies[num-i,:,:]
plt.imshow(fliph)
(1856, 2784, 3)
1855
<matplotlib.image.AxesImage at 0x23c52d5d510>
Let's use the balloons image for the next homework problem.
#balloons = imread('pexels-tabitha-mort-693490.jpg')
plt.imshow(balloons);
# Todo: Flip a copy of 'balloons' along the horizontal axis, i.e. a vertical flip
flipv = balloons.copy()
# get counter from shape tuple to int
num = flipv.shape[1] - 1 #adjust for zero offset
for i in range(0,num):
#print('i is: ',i)
flipv[:,i,:] = balloons[:,num-i,:]
# Todo: Flip a copy of the vertically flipped image along the vertical axis, i.e. a horizontal flip
fliph = flipv.copy()
# get counter from shape tuple to int
num = fliph.shape[0] - 1 #adjust for zero offset
print(num)
for i in range(0,num):
#print('i is: ',i)
fliph[i,:,:] = balloons[num-i,:,:]
# Todo: Show the final image
plt.imshow(fliph)
3839
<matplotlib.image.AxesImage at 0x23c4d44ce20>
The Python Pillow Library is a fork of an older library called PIL that was only supported in Python 2 and has since been discontinued. Pillow provides image processing features that are similar to Photoshop, such as:
First, let's open our veggies image with Pillow. Notice when this next cell is run, the image will open up as a .png file in a different window.
Notice when you run the following cell the image pops up in a different window as a png file.</font>
pil_veg = Image.open('M8 Images/pexels-maria-orlova-4946998.jpg')
# note opens image up as .png in a different window
pil_veg.show()
# can display in JN just by using the object name
pil_veg